﻿from math import sin, cos, hypot, atan2, radians
from random import randint as random

from locals      import *
from projectile  import *
from simple_math import *

from pygame import draw, rect

class Mage:
    def __init__(self, color, position, mage_id):
        self.mage_id                = mage_id
        self.color                  = color
        self.eyecolor               = (255-color[0], 255-color[1], 255-color[2])
        self.handcolor              = (color[0]/2, color[1]/2, color[2]/2)
        self.position               = position
        self.orientation            = radians(random(0, 360))
        self.hit_points             = MAGE_HP
        self.cooldown               = 0
        self.invincibility_cooldown = 0
        self.rect                   = rect.Rect(position, (SWORD_END_RADIUS*2+1, SWORD_END_RADIUS*2+1))
        self.strike_cooldown        = 0
        self.strike_type            = None
        self.swinging_sword         = 0
        self.update_required        = False

    ##############
    # Properties #
    ##############
    @property
    def iPos(self):
        '''Created for compatibility reasons with more recent versions of Pygame
        which do not accept floating point arguments for their primitive drawing
        functions.'''
        (x, y) = self.position
        return (int(round(x)), int(round(y)))

    @property
    def iRect(self):
        '''Created for compatibility reasons with more recent versions of Pygame
        which do not accept floating point arguments for their primitive drawing
        functions.'''
        self.rect.center = self.iPos
        return self.rect

    #############################
    # The big updating function #
    #############################
    def act(self, actions, projectiles, effects, mages):
        if self.swinging_sword:
            if ((self.swinging_sword >= SWING_STEP_TIME) and
               (self.swinging_sword < SWING_STEP_TIME*2)):
                #Verificar se o circulo se encontra no arco
                for p in projectiles:
                    if point_point_distance(self.position, p.position) < SWORD_END_RADIUS + p.radius:
                        p.deflect(self.orientation)
                for m in [a for a in mages if a!=self]:
                    if point_point_distance(self.position, m.position) < SWORD_END_RADIUS + MAGE_RADIUS:
                        m.hit(self.orientation)
                    
            self.swinging_sword -= 1
            return
        if self.cooldown > 0:
            return
        if len(actions) == 0:
            return
        self.cooldown = ATTACK_COOLDOWN
        if actions[0] == SWORD:
            self.swinging_sword = SWING_TIME
            self.cooldown       = SWING_TIME
            return
        if actions[0] == FIREBALL:
            projectiles.append(Fireball(self.position, self.orientation))
#            effects.append(Fireball_MagicCircle(self.position))
        if actions[0] == E_BOLT:
            projectiles.append(Electric_Bolt(self.position, self.orientation))
        targets = [m for m in mages if m != self]
        target = targets[0]
        d = point_point_distance(self.position, target.position)
        for t in targets[1:]:
            if d > point_point_distance(self.position, t.position):
                target = t
        if actions[0] == MMISSILE:
            projectiles.append(Magic_Missile(self.position, self.orientation, target))

    def get_rect(self):
        self.rect.center = self.position
        return self.rect
        
    
    def hit(self, projectile_type):
        if self.strike_type:
            return
        if self.invincibility_cooldown > 0:
            return
        self.swinging_sword  = 0
        self.hit_points     -= 1
        self.strike_type     = projectile_type
        self.strike_cooldown = STRIKE_COOLDOWN
    
    def turn(self, xspeed, yspeed):
        # Prevent the character from turning around if it has just attacked,
        # been hit or isn't putting enough tilt on the directional pad.
        if self.cooldown: return
        if self.strike_type: return
        if hypot(xspeed, yspeed) < 0.1: return

        self.orientation = atan2(yspeed, xspeed)

    def get_next_position(self, xspeed, yspeed):
        xd = xspeed * MAGE_SPEED
        yd = yspeed * MAGE_SPEED
        return (self.position[0]+xd, self.position[1]+yd)

    def move(self, position):
        '''Updates the position of the mage. Does not consider collision.
        Deals with every one of the mage's cooldowns
        '''
        if self.hit_points == 0:
            return
        if self.invincibility_cooldown > 0:
            self.invincibility_cooldown -= 1
        if self.strike_cooldown > 1:
            self.strike_cooldown -= 1
            return
        elif self.strike_cooldown == 1:
            self.strike_cooldown        = 0
            self.strike_type            = None
            self.cooldown               = 0
            self.invincibility_cooldown = INVINCIBILITY_COOLDOWN
            return
        if self.cooldown > 0:
            self.cooldown -= 1
            return
        self.position = position

    def draw(self, surface):
        body_pos = self.iPos
        l_eye_pos = (body_pos[0] + int(EYE_OFFSET * cos(self.orientation + EYE_ANGLE)),
                     body_pos[1] + int(EYE_OFFSET * sin(self.orientation + EYE_ANGLE)))
        r_eye_pos = (body_pos[0] + int(EYE_OFFSET * cos(self.orientation - EYE_ANGLE)),
                     body_pos[1] + int(EYE_OFFSET * sin(self.orientation - EYE_ANGLE)))


        draw.circle(surface, BLACK, body_pos, MAGE_RADIUS_2)
        #If the mage is hurt
        if self.strike_type:
            draw.circle(surface, BURNT_COLOR, body_pos, MAGE_RADIUS)
            draw.circle(surface, BLACK, l_eye_pos, EYE_SIZE_2)
            draw.circle(surface, self.eyecolor, l_eye_pos, EYE_SIZE)
            draw.circle(surface, BLACK, r_eye_pos, EYE_SIZE_2)
            draw.circle(surface, self.eyecolor, r_eye_pos, EYE_SIZE)

            #Draw hitpoints
            if self.hit_points > 0:
                hitpoints_pos = (body_pos[0]-HP_SIZE_4*2, body_pos[1]-BARRIER_RADIUS-HP_SIZE_2)
                for x in xrange(0, self.hit_points):
                    draw.circle(surface, BLACK, (hitpoints_pos[0]+x*HP_SIZE_4, hitpoints_pos[1]), HP_SIZE_2)
                    draw.circle(surface, INVINCIBLE_COLOR, (hitpoints_pos[0]+x*HP_SIZE_4, hitpoints_pos[1]), HP_SIZE)
                for x in xrange(self.hit_points, MAGE_HP):
                    draw.circle(surface, BLACK, (hitpoints_pos[0]+x*HP_SIZE_4, hitpoints_pos[1]), HP_SIZE_2)
                    draw.circle(surface, BURNT_COLOR, (hitpoints_pos[0]+x*HP_SIZE_4, hitpoints_pos[1]), HP_SIZE)

        #If the mage has a shield
        elif self.invincibility_cooldown > 0:
            if self.invincibility_cooldown > INVINCIBILITY_COOLDOWN/2:
                draw.circle(surface, INVINCIBLE_COLOR, body_pos, MAGE_RADIUS)
            else:
                draw.circle(surface,
                            average_color(INVINCIBLE_COLOR, self.invincibility_cooldown,
                                          self.color, (INVINCIBILITY_COOLDOWN/2) - self.invincibility_cooldown),
                            body_pos, MAGE_RADIUS)
            draw.circle(surface, BLACK, l_eye_pos, EYE_SIZE_2)
            draw.circle(surface, self.eyecolor, l_eye_pos, EYE_SIZE)
            draw.circle(surface, BLACK, r_eye_pos, EYE_SIZE_2)
            draw.circle(surface, self.eyecolor, r_eye_pos, EYE_SIZE)

            #If the shield is fading
            if self.invincibility_cooldown > INVINCIBILITY_COOLDOWN/3:
                draw.circle(surface, BARRIER_COLOR, body_pos, BARRIER_RADIUS, 1)
            else:
                draw.circle(surface,
                            average_color(BARRIER_COLOR, self.invincibility_cooldown,
                                          BLACK, (INVINCIBILITY_COOLDOWN/2) - self.invincibility_cooldown),
                            body_pos, BARRIER_RADIUS, 1)
        else:
            draw.circle(surface, self.color, self.iPos, MAGE_RADIUS)
            draw.circle(surface, BLACK, l_eye_pos, EYE_SIZE_2)
            draw.circle(surface, self.eyecolor, l_eye_pos, EYE_SIZE)
            draw.circle(surface, BLACK, r_eye_pos, EYE_SIZE_2)
            draw.circle(surface, self.eyecolor, r_eye_pos, EYE_SIZE)

        if self.swinging_sword:
            (start_pos, end_pos) = self.sword_line()
            draw.line(surface, SWORD_COLOR, start_pos, end_pos)
            draw.circle(surface, self.handcolor, start_pos, HAND_SIZE)
            if ((self.swinging_sword >= SWING_STEP_TIME) and
               (self.swinging_sword < SWING_STEP_TIME*2)):
                r = rect.Rect((0,0), (2*SWORD_END_RADIUS, 2*SWORD_END_RADIUS))
                r.center = body_pos
                draw.arc(surface, SWORD_COLOR, r,
                         -self.orientation - SWORD_ARC_OVER_2,
                         -self.orientation + SWORD_ARC_OVER_2)
                              
    def sword_line(self):
        '''Calculates and return the position of the sword'''
        if self.swinging_sword >= SWING_STEP_TIME*2:
            angle = self.orientation - SWORD_ARC_OVER_2
        else:
            angle = self.orientation + SWORD_ARC_OVER_2
        start_pos = (int(self.position[0] + SWORD_START_RADIUS*cos(angle)),
                     int(self.position[1] + SWORD_START_RADIUS*sin(angle)))
        end_pos   = (int(self.position[0] + SWORD_END_RADIUS*cos(angle)),
                     int(self.position[1] + SWORD_END_RADIUS*sin(angle)))
        return (start_pos, end_pos)

